<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<title>Error log</title>
<style>
body
{
    font-family: Verdana;
    font-size: 70%;
    background-color: #fff;
}

button, input
{
    font-size: 100%;
}

a
{
    color: #0033CC;
}

a:visited
{
    color: Navy;
}

a:hover
{
    color: #FF6600;
}

img 
{
    border: none;
}

pre, code
{
    font-family: "Courier New", Courier;
}

table
{
    width: 100%;
    border-collapse: collapse;
}

td
{
    border: solid 1px silver;
    padding: 0.4em;
    vertical-align: top;
}

th
{
    text-align: left;
    background-color: #0A6CCE;
    padding: 0.4em;
    color: White;
    vertical-align: top;
    border: solid 1px silver;
}

.odd-row
{
    background-color: #e9e9e9;
}

.type-col
{
    font-weight: bold;
}

.code-col, .date-col, .time-col
{
    text-align: right;
}

#ErrorDetail
{
    font-size: 110%;
    background-color: #ffffcc;
    padding: 1em;
    width: 100%;
}

.st-type, .st-param-type
{
    color: #00008B;
}

.st-param-name
{
    color: #666;
}

.st-method
{
    color: #008B8B;
    font-weight: bolder;
}

.st-file, .st-line
{
    color: #8B008B;
}

@media screen
{
    #ErrorDetail
    {
        overflow: scroll;
    }
}

#ErrorTitle
{
    font-weight: bold;
    font-size: 120%;
}

#ErrorType, #ErrorMessage
{
    display: block;
}

#ErrorTypeMessageSeparator
{
    display: none;
}

.key-col
{
    font-weight: bold;
}

h1
{
    font-family: Verdana;
    font-weight: normal;
    color: #0A6CCE;
    font-size: 175%;
}

.table-caption
{
    background-color: navy;
    margin: 0;
    color: white;
    padding: 0.4em;
    font-weight: bold;
}

@media screen 
{
    .scroll-view
    {
        width: 100%;
        overflow: scroll;
    }
}

#SpeedList 
{
    margin: 0;
    list-style-type: none;
    text-transform: uppercase;
    font-size: 80%;
    padding: 0.25em 0;
    color: #fff;
    background-color: #aaa;
    border-top: solid 1px #aaa;
    border-bottom: solid 1px #aaa;
}

#SpeedList a
{
    text-decoration: none;    
    padding: 0.25em 1em;
    border: solid 1px #aaa;
    border-right: solid 1px #fff;
    color: #fff;
}

#SpeedList a:hover
{
    background-color: #fff;
    color: #444;
    border: solid 1px #aaa;
}

#SpeedList li
{
    display: inline;
}

@media print
{
    #SpeedList
    {
        display: none;
    }
}

[v-cloak] { display: none !important; }
a[disabled="disabled"] { color: #696969; cursor: not-allowed; }
</style>
</head>
<body>

<div id="wrap" v-cloak>
    <router-view></router-view>
</div>

<template id="error_record_detail">
<div>
<h1 id="PageTitle">{{errorInfo.message}}</h1>
<ul id="SpeedList">
    <li><a v-bind:href="errorInfosPage">Errors</a></li>
    <li><a href="https://github.com/JerryXia/BizHttpRequestTest/tree/master/source/devhelper/src/main/java/com/github/jerryxia/devhelper/elmah" target="_blank">About</a></li>
</ul>
<p id="ErrorTitle">
    <span id="ErrorType">{{errorInfo.type}}</span>
    <span id="ErrorTypeMessageSeparator">: </span>
    <span id="ErrorMessage">{{errorInfo.message}}</span>
</p>
<pre id="ErrorDetail">{{errorInfo.detail}}</pre>
<p id="ErrorLogTime">Logged on {{errorInfo.timeStamp|timeStamp('yyyy-MM-dd HH:mm:ss')}}</p>
<div id="ServerVariables">
    <p class="table-caption">Request Info</p>
    <div class="scroll-view">
        <table cellspacing="0" border="0" style="border-collapse:collapse;">
           <thread>
               <tr>
                   <th class="name-col" style="white-space:nowrap;">Name</th><th class="value-col" style="white-space:nowrap;">Value</th>
               </tr>
           </thread>
           <tbody>
               <tr><td>method</td><td>{{errorInfo.method}}</td></tr>
               <tr><td>requestAndQuery</td><td>{{ errorInfo | showRequestAndQuery }}</td></tr>
               <tr><td>contentType</td><td>{{ errorInfo.contentType }}</td></tr>
               <tr><td>form parameter</td><td>{{ errorInfo.parameterMap | parameterFormater}}</td></tr>
               <template v-for="(value, key) in errorInfo.headers">
                 <tr v-for="v in value">
                   <td>{{key}}</td><td>{{v}}</td>
                 </tr>
               </template>
           </tbody>
        </table>
    </div>
</div>
<p id="Footer"><span>Powered by <a href="https://github.com/JerryXia/BizHttpRequestTest/tree/master/source/devhelper/src/main/java/com/github/jerryxia/devhelper/elmah" target="_blank">ELMAH</a>, version {{libVersion}}. </span>Server datetime is {{serverDate|timeStamp('yyyy-MM-dd HH:mm:ss')}}.</p>
</div>
</template>

<script src="https://cdn.bootcss.com/jquery/2.2.2/jquery.js"></script>
<script src="https://cdn.bootcss.com/vue/2.5.9/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<script>const PATH_PREFIX='';</script>
<script>
'use strict';

var utils = { __version__: 'testVersion 0.0.1' };
utils.format = function () {
    if (arguments.length == 0) return null;
    var str = arguments[0];
    for (var i = 1; i < arguments.length; i++) {
        var regExp = new RegExp('\\{' + (i - 1) + '\\}', 'gm');
        str = str.replace(regExp, arguments[i])
    }
    return str;
};
//解析location.search
utils.getQueryStringArgs = function () {
    var qs = (window.location.search.length > 0 ? window.location.search.substring(1) : ""); //去除？
    var args = {},
    items = qs.length ? qs.split("&") : [];
    var item = null,
    name = null,
    value = null,
    i = 0,
    len = items.length;
    for (i = 0; i < len; i++) {
        item = items[i].split("=");
        name = decodeURIComponent(item[0]);
        value = decodeURIComponent(item[1]);
        if (name.length) {
            args[name] = value;
        };
    }
    return args;
};
utils.formPost = function(url, postData, successCallback, errorCallback, completeCallback) {
    $.ajax({
        method: 'post',
        url: url,
        contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
        data: postData,
        success: successCallback,
        error: function (jqXHR, textStatus, errorThrown) {
            console.info(jqXHR); 
            console.info(textStatus); 
            console.error(errorThrown);
            if(typeof errorCallback !== 'undefined') {
                errorCallback();
            }
        },
        complete: function (jqXHR, textStatus) {
            if(typeof completeCallback !== 'undefined') {
                completeCallback(jqXHR, textStatus);
            }
        }
    });
};


const errorRecordDetail = {
    template: '#error_record_detail',
    data: function () {
        return {
            contextPath: '',
            server: '',
            serverDate: 0,
            libVersion: '',
            
            errorInfosPage: PATH_PREFIX + '/',

            errorInfo: {
                contentType: null,
                detail: '',
                headers: {},
                host: '',
                id: '',
                ip: '',
                message: '',
                type: '',
                method: '',
                parameterMap: {},
                queryString: '',
                requestURI: '',
                requestURL: '',
                source: '',
                statusCode: 0,
                timeStamp: 0,
                user: null
            },
            queryId: ''
        }
    },
    created: function () {
        console.log('errorRecordDetail created');
        this.fetchData();
    },
    destroyed: function () {
        console.log('errorRecordDetail destroyed');
    },
    watch: {

    },
    computed: {

    },
    methods: {
        fetchData: function () {
            console.log('errorRecordDetail fetchData');
            let that = this;
            that.isFetchingData = true;

            let queryParams = utils.getQueryStringArgs();
            that.queryId = queryParams.id || '';
            utils.formPost(PATH_PREFIX + '/detail.json', { id: that.queryId }, function(res) {
                if(res) {
                    that.contextPath = res.contextPath;
                    that.server = res.server;
                    that.serverDate = res.serverDate;
                    that.libVersion = res.libVersion;
                    that.errorInfo = res.errorInfo;
                }
            }, function(jqXHR, textStatus, errorThrown) {
                // 
            }, function(jqXHR, textStatus) {
                that.isFetchingData = false;
                // "success", "notmodified", "nocontent", "error", "timeout", "abort", or "parsererror"
                console.log('errorRecordDetail errors.json:' + textStatus);
            });
        }
    }
};
const router = new VueRouter({
    mode: 'history',
    routes: [
        { path: PATH_PREFIX + '/detail', component: errorRecordDetail }
        //{ path: PATH_PREFIX + '/detail', component: detail }
    ]
});
const app = new Vue({
    router: router
}).$mount('#wrap');


if (typeof Date.prototype.format == 'undefined') {
    Date.prototype.format = function (mask) {
        var d = this;
        var zeroize = function (value, length) {
            if (!length) length = 2;
            value = String(value);
            for (var i = 0, zeros = ''; i < (length - value.length); i++) {
                zeros += '0';
            }
            return zeros + value;
        };

        return mask.replace(/"[^"]*"|'[^']*'|\b(?:d{1,4}|m{1,4}|yy(?:yy)?|([hHMstT])\1?|[lLZ])\b/g, function ($0) {
            switch ($0) {
                case 'd': return d.getDate();
                case 'dd': return zeroize(d.getDate());
                case 'ddd': return ['Sun', 'Mon', 'Tue', 'Wed', 'Thr', 'Fri', 'Sat'][d.getDay()];
                case 'dddd': return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][d.getDay()];
                case 'M': return d.getMonth() + 1;
                case 'MM': return zeroize(d.getMonth() + 1);
                case 'MMM': return ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][d.getMonth()];
                case 'MMMM': return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][d.getMonth()];
                case 'yy': return String(d.getFullYear()).substr(2);
                case 'yyyy': return d.getFullYear();
                case 'h': return d.getHours() % 12 || 12;
                case 'hh': return zeroize(d.getHours() % 12 || 12);
                case 'H': return d.getHours();
                case 'HH': return zeroize(d.getHours());
                case 'm': return d.getMinutes();
                case 'mm': return zeroize(d.getMinutes());
                case 's': return d.getSeconds();
                case 'ss': return zeroize(d.getSeconds());
                case 'l': return zeroize(d.getMilliseconds(), 3);
                case 'L': var m = d.getMilliseconds();
                    if (m > 99) m = Math.round(m / 10);
                    return zeroize(m);
                case 'tt': return d.getHours() < 12 ? 'am' : 'pm';
                case 'TT': return d.getHours() < 12 ? 'AM' : 'PM';
                case 'Z': return d.toUTCString().match(/[A-Z]+$/);
                // Return quoted strings with the surrounding quotes removed
                default: return $0.substr(1, $0.length - 2);
            }
        });
    };
};

Vue.filter('timeStamp', function (value, formater) {
    return new Date(value).format(formater);
});
Vue.filter('showRequestAndQuery', function (apiRecord) {
    let val = apiRecord.requestURI + ( (apiRecord.queryString && apiRecord.queryString.length>0) ? '?'+ apiRecord.queryString : '');
    return val;
});
Vue.filter('parameterFormater', function (obj) {
    let formaterObj = {};
    //obj = JSON.parse(JSON.stringify(obj));
    if (obj) {
        for (let k in obj) {
            let v = obj[k];
            if (Array.isArray(v)) {
                if (v.length === 1) {
                    formaterObj[k] = v[0];
                } else {
                    formaterObj[k] = v;
                }
            } else {
                formaterObj[k] = v;
            }
        }
    }
    return JSON.stringify(formaterObj);
});
</script>
</body>
</html>